tags:
- Cpp
The pIMPL Idiom in C++ (ENG)
In many code design patterns, people make all the declarations in the hpp
file and definitions in the cpp
file. This is called separating the interface and the implementation. Here’s an example:
// myLib.hpp
#ifndef MYLIB_HPP
#define MYLIB_HPP
class myLib {
private:
int num;
public:
myLib(int n); // Constructor taking an int parameter
~myLib() = default; // Destructor doing nothing
int add(int n); // Method taking an int parameter
int sub(int n); // Method taking an int parameter
};
#endif
// myLib.cpp
#include "myLib.hpp"
myLib::myLib(int n) : num{n} {}
int myLib::add(int n) {
num += n;
return num;
}
int myLib::sub(int n) {
num -= n;
return num;
}
After compilation, the user will not see any implementation details of the library, that is, our cpp
file. This approach has several benefits, such as encapsulation and ease of use. But, sometimes it is better not to expose any interface details about the implementation. In such cases, we can introduce the pimpl idiom.
We will demonstrate how our last code example above applies the pimpl idiom. With the pimpl idiom, the example above will look like:
// myLib.hpp
#ifndef MYLIB_HPP
#define MYLIB_HPP
class myLibImpl; // Forward declaration of the implementation class
class myLib {
private:
myLibImpl* pImpl; // Pointer to the implementation class
public:
void operation(int n);
myLib(int n); // Constructor taking an int parameter
~myLib();
int add(int n);
int sub(int n);
// Rule of five...
};
#endif
// myLib.cpp
#include "myLib.hpp"
#include <iostream>
// Implementation of the myLibImpl class
class myLibImpl {
public:
int num;
myLibImpl(int n) : num{n} {}
int add(int n) {
num += n;
return num;
}
int sub(int n) {
num -= n;
return num;
}
void operation(int n) {
std::cout << "Operation with value: " << n << std::endl;
}
};
// Constructor
myLib::myLib(int n) : pImpl{new myLibImpl(n)} {}
// Destructor
myLib::~myLib() {
delete pImpl;
}
// Operation method
void myLib::operation(int n) {
pImpl->operation(n);
}
// Rule of five implementations...
We see, everything about the implementation is in one translation unit, and what we have in myLib.hpp
is really an interface to myLibImpl
.
You can see the code is now safer to use, but one level of indirection is added, which may impact performance.
I am not done with the pIMPL design pattern.